package com.starbaba.core.hibernate;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.starbaba.core.web.constant.WebConstants;
import com.starbaba.core.web.struts2.BaseListVO;

@SuppressWarnings("all")
public class HibernateDAO{

	
	protected Logger logger = LoggerFactory.getLogger(getClass());

	protected SessionFactory sessionFactory;
	
	protected Class<?> entityClass;

	/**
	 * HibernateDAO构造方法
	 * eg.
	 * UserDao<User, Long> userDao = new HibernateDAO<User, Long>(sessionFactory, User.class);
	 */
	public HibernateDAO(SessionFactory sessionFactory, Class<?> entityClass) {
		this.sessionFactory = sessionFactory;
		this.entityClass = entityClass;
	}	
	public HibernateDAO(SessionFactory sessionFactory) {
	    this.sessionFactory = sessionFactory;
	}   
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	public Session getSession(){
		return sessionFactory.getCurrentSession();
	}
	
	/**
	 * 清除缓存内容
	 */
	@SuppressWarnings("deprecation")
	public void evict(){
		this.getSessionFactory().evict(entityClass);		
	}
	
	/**
	 * 从缓存中清除该对象
	 * @param id
	 */
	@SuppressWarnings("deprecation")
	public void evict(final Serializable id){
		Assert.notNull(id);
		this.getSessionFactory().evict(entityClass, id);
	}
	
	/**
	 * 保存新增或修改的对象.
	 * @param entity
	 * @return
	 */
	public Object save(final Object entity){
		Assert.notNull(entity, "save entity: {}");
		getSession().save(entity);
		return entity;
	}
	
	/**
	 * 保存新增或修改的对象.
	 * @param entity
	 * @return
	 */
	public Object saveOrUpdate(final Object entity) {
		Assert.notNull(entity, "save or update entity: {}");
		getSession().saveOrUpdate(entity);
		return entity;
	}
	
	/**
	 * 删除对象.
	 * @param entity 对象必须是session中的对象或含id属性的transient对象.
	 */
	public void deleteByVO(final Object entity) {
		Assert.notNull(entity, "delete entity: {}");
		getSession().delete(entity);
	}
	
	/**
	 * 删除对象.
	 * @param entity 对象必须是session中的对象或含pk属性的transient对象.
	 */
	public void deleteByPK(final Serializable pk) {
		Assert.notNull(pk, "delete pk: {}");
		Object obj = findByPK(pk);
		if(obj!=null)
			getSession().delete(obj);
	}
	
	/**
	 * 按id获取对象.
	 */
	public Object findByPK(final Serializable pk) {
		Assert.notNull(pk);
		return getSession().get(entityClass, pk);
	}
	
	/**
	 * 按模型进行查询
	 * @param entity
	 * @return
	 */
	public List<?> findByExample(final Object entity){
		Assert.notNull(entity);
		return getSession().createCriteria(entityClass).add(Example.create(entity)).list();
	}
	
	/**
	 *	获取全部对象. 
	 */
	public List<?> findAll() {
		return findByCriteria();
	}

	/**
	 * 按属性查找对象列表,匹配方式为相等.
	 */
	public List<?> findByProperty(final String propertyName, final Object value) {
		Assert.hasText(propertyName);
		Criterion criterion = Restrictions.eq(propertyName, value);
		return findByCriteria(criterion);
	}

	/**
	 * 按属性查找唯一对象,匹配方式为相等.
	 */
	public Object findUniqueByProperty(final String propertyName, final Object value) {
		Assert.hasText(propertyName);
		Criterion criterion = Restrictions.eq(propertyName, value);
		return createCriteria(criterion).uniqueResult();
	}
	
	/**
	 * 按Criteria查询对象列表.	 * 
	 * @param criterions 数量可变的Criterion, 该方法可以不带参数
	 */
	public List<?> findByCriteria(final Criterion... criterions) {
		return createCriteria(criterions).list();
	}
	
	/**
	 * 根据Criterion条件创建Criteria
	 * 返回对象类型不是Entity时可用此函数灵活查询
	 * @param criterions 数量可变的Criterion
	 */
	public Criteria createCriteria(final Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}	
	
	/**
	 * 执行SQL
	 * @param sql 
	 * @param values “?”参数数组
	 * @return 执行条数
	 *
	 * @Copyright: Copyright (c) 2006
	 * @Company: Revenco Tech Ltd.
	 * @anthor chenminhui<chenminhui@revenco.com>
	 * @since 2014-11-18 下午03:55:48
	 * @version V1.0
	 */
	public int executeSQLUpdate(final String sql,final Object... values){
		SQLQuery sqlQuery=getSession().createSQLQuery(sql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				sqlQuery.setParameter(i, values[i]);
			}
		}
		return sqlQuery.executeUpdate();
	}
	
	/**
	 * 执行HQL
	 * @param hql 
	 * @param values “?”参数数组
	 * @return 执行条数
	 *
	 * @Copyright: Copyright (c) 2006
	 * @Company: Revenco Tech Ltd.
	 * @anthor chenminhui<chenminhui@revenco.com>
	 * @since 2014-11-18 下午03:55:48
	 * @version V1.0
	 */
	public int executeHQLUpdate(final String hql,final Object... values){
		Query query = createQuery(hql, values);
		return query.executeUpdate();
	}
	
	public int executeHQLUpdate(final String hql,final Map<String,Object> params){
		Query query = createQuery(hql, params);
		return query.executeUpdate();
	}
	
	public List findBySQL(final String sql , final Map<String, Type> scalars , final Object... values){
		SQLQuery query = getSession().createSQLQuery(sql);
		
		if(scalars!=null){
			for(String key : scalars.keySet()){
				query.addScalar(key , scalars.get(key) );
			}
		}
		
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query.list();
	}
	
	
	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param values 数量可变的参数
	 */
	public List<?> findByHQL(final String hql, final Object... values) {
		return createQuery(hql, values).list();
	}

	/**
	 * 按HQL查询唯一对象.
	 */
	public Object findUniqueByHQL(final String hql, final Object... values) {
		return createQuery(hql, values).uniqueResult();
	}	
	
	/**
	 * 根据查询HQL与参数列表创建Query对象
	 * 返回对象类型不是Entity时可用此函数灵活查询
	 * @param values 数量可变的参数
	 */
	public Query createQuery(final String queryString, final Object... values) {
		Assert.hasText(queryString);
		Query query = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}
	/**
	 * 按照params进行查询
	 * @param hql
	 * @param params
	 * @return
	 */
	public List<?> findByHQL(final String hql, final Map<String, Object> params){
		return createQuery(hql, params).list();
	}
	
	/**
	 * 按照params进行查询
	 * @param hql
	 * @param params
	 * @return
	 */
	public Object findUniqueByHQL(final String hql, final Map<String, Object> params){
		return createQuery(hql, params).uniqueResult();
	}
	
    public Page findPageBySql(Page page,final String sql, final Object... values){
        int totalItems = countSqlResult(sql, values);
        page.setTotalCount(totalItems);
        Query query = createSQLQuery(sql, values);
        page.setDatas(query.setFirstResult((page.getPageNo()-1)*page.getPageSize()).setMaxResults(page.getPageSize()).list());
        return page;
    }
    public List findByAliasSql(final String sql, Class<?> voClass, final Object... values) {
    	Query query = getSession().createSQLQuery(sql); //.setResultTransformer(Transformers.aliasToBean(voClass));
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        query.setResultTransformer(Transformers.aliasToBean(voClass));
        query.setFirstResult(0).setMaxResults(40);
        return query.list();
    	 
    }
    
    public Page findPageBySql(Page page,final String sql, Class<?> voClass, final Object... values){
    	int totalItems = countSqlResult(sql, values);
        page.setTotalCount(totalItems);
        Query query = createSQLQuery(sql, values);
        query.setResultTransformer(new ExtAliasToBeanResultTransformer(voClass));
//        query.setResultTransformer(Transformers.aliasToBean(voClass));
        page.setDatas(query.setFirstResult((page.getPageNo()-1)*page.getPageSize()).setMaxResults(page.getPageSize()).list());
        return page;
    }
	
	/**
	 * 根据查询HQL与参数列表创建Query对象
	 * 返回对象类型不是Entity时可用此函数灵活查询
	 * @param 参数Map
	 */
	public Query createQuery(final String queryString, final Map<String, Object> params){
		Assert.hasText(queryString);
		Query query = getSession().createQuery(queryString);
		if(null != params && params.size() > 0){
			for(Map.Entry<String, Object> entry : params.entrySet()){
				Object value = entry.getValue();
				if(value instanceof Object[]){
					query.setParameterList(entry.getKey(), (Object[])entry.getValue());
				}else{
					query.setParameter(entry.getKey(), entry.getValue());
				}
			}
		}
		return query;
	}
	
	/**
	 * 查询表中propertyName字段最大值
	 * @param propertyName
	 */
	public Object getMaxValue(String propertyName) {
		Assert.hasText(propertyName, "propertyName is null");
		return createCriteria().setProjection(Projections.max(propertyName)).uniqueResult();
	}
	
	/**
	 * 单表查询
	 * @param listVO
	 * @param type
	 * @return
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	public Page singleQuery(Object params, final QueryType type) throws Exception{
		Page page = new Page();
		String preFix = "";
		BaseListVO listVO = (BaseListVO)params;
		this.setPageInfo(page, listVO);
		
		StringBuffer countHQL = new StringBuffer();
		countHQL.append(" SELECT COUNT(*) FROM ");
		countHQL.append(entityClass.getName()).append(" ").append(preFix).append(" ");
		
		StringBuffer selectHQL = new StringBuffer();
		selectHQL.append(" FROM ");
		selectHQL.append(entityClass.getName()).append(" ").append(preFix).append(" ");
		
		List<Condition> conditionList = null;
		try{
			conditionList = ConditionBuilder.build(listVO, preFix);
		}catch(Exception ex){
			throw new Exception("组装查询条件出错");
		}
		
		StringBuffer conditionSQL = new StringBuffer();
		for(Condition condition : conditionList){
			conditionSQL.append(condition.getExpression()).append(" AND ");
		}
		if(conditionSQL.length() > 4){
			conditionSQL.delete(conditionSQL.length() - 4, conditionSQL.length() - 1);
			countHQL.append(" WHERE ").append(conditionSQL);
			selectHQL.append(" WHERE ").append(conditionSQL);
		}
		
		if (type == QueryType.ALL || type == QueryType.COUNT) {
			Query query = getSession().createQuery(countHQL.toString());			
			for(Condition condition : conditionList){
				for(Map.Entry<String, Object> entry : condition.getParams().entrySet()){
					query.setParameter(entry.getKey(), entry.getValue());
				}
			}
			Object countValue = query.uniqueResult();
			if(null != countValue && !"".equals(countValue)){
				page.setTotalCount(Integer.parseInt(String.valueOf(countValue)));
			}
		}
		
		if (type == QueryType.ALL || type == QueryType.DATA) {
			StringBuffer orderBySQL = new StringBuffer();
			String orderBy = listVO.getOrderBy();
			String order = listVO.getOrder();
			if(null != orderBy && !"".equals(orderBy.trim())){
				if (preFix.trim().length() > 0) {
					preFix = preFix + ".";
				}
				orderBySQL.append(" ORDER BY ");
				//orderBySQL.append(preFix + "." + orderBy + " ");
				orderBySQL.append(preFix + orderBy + " ");
				if(BaseListVO.ASC.equalsIgnoreCase(order.trim()) || BaseListVO.DESC.equalsIgnoreCase(order.trim())){
					orderBySQL.append(order);
				}
			}
			
			if(orderBySQL.length() > 0){
				selectHQL.append(orderBySQL);
			}		
			//System.out.println("selectHQL==="+selectHQL);
			Query query = getSession().createQuery(selectHQL.toString());
			for(Condition condition : conditionList){
				for(Map.Entry<String, Object> entry : condition.getParams().entrySet()){
					query.setParameter(entry.getKey(), entry.getValue());
				}
			}
			this.setPageParameter(query, listVO);
			List datas = query.list();
			page.setDatas(datas);
		}		
		return page;
	}
	
	/**
	 * 多表查询
	 * @param listVO
	 * @param type
	 * @return
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	public Page multiQuery(Object params, final QueryType type) throws Exception{
		Page page = new Page();
		BaseListVO listVO = (BaseListVO)params;
		this.setPageInfo(page, listVO);
		
		MultiHQLBuilder hqlBuilder = new MultiHQLBuilder((MultiParameter)params);		
		List<Condition> conditionList = hqlBuilder.getConditionList();		
		StringBuffer countHQL = new StringBuffer();
		countHQL.append(hqlBuilder.getCountHQL());
		
		StringBuffer selectHQL = new StringBuffer();
		selectHQL.append(hqlBuilder.getSelectHQL());
		
		StringBuffer conditionSQL = new StringBuffer();
		for(Condition condition : conditionList){
			conditionSQL.append(condition.getExpression()).append(" AND ");
		}
		if(conditionSQL.length() > 4){
			conditionSQL.delete(conditionSQL.length() - 4, conditionSQL.length() - 1);
			countHQL.append(" AND ").append(conditionSQL);
			selectHQL.append(" AND ").append(conditionSQL);
		}
		
		if (type == QueryType.ALL || type == QueryType.COUNT) {
			Query query = getSession().createQuery(countHQL.toString());
			for(Condition condition : conditionList){
				for(Map.Entry<String, Object> entry : condition.getParams().entrySet()){
					query.setParameter(entry.getKey(), entry.getValue());
				}
			}
			Object countValue = query.uniqueResult();
			if(null != countValue && !"".equals(countValue)){
				page.setTotalCount(Integer.parseInt(String.valueOf(countValue)));
			}
		}
		
		if (type == QueryType.ALL || type == QueryType.DATA) {				
			Query query = getSession().createQuery(selectHQL.toString());
			for(Condition condition : conditionList){
				for(Map.Entry<String, Object> entry : condition.getParams().entrySet()){
					query.setParameter(entry.getKey(), entry.getValue());
				}
			}
			this.setPageParameter(query, listVO);
			List datas = query.list();
			page.setDatas(datas);
		}		
		return page;
	}
	
	/**
	 * 选择查询种类,单表查询或多表查询
	 * @param params
	 * @param type
	 * @return
	 * @throws Exception
	 */
	public Page query(Object params, final QueryType type) throws Exception{
		if(params instanceof MultiParameter){
			return this.multiQuery(params, type);
		}
		return this.singleQuery(params, type);
	}
	
	/**
	 * 默认查询数据总数和加载指定页面数据
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public Page query(Object params) throws Exception{
		return this.query(params, QueryType.ALL);
	}
	

	/**
	 * 设置分页信息
	 * @param page
	 * @param listVO
	 */
	public void setPageInfo(Page page, BaseListVO listVO){
		int pageNo = listVO.getPageNo();
		int pageSize = listVO.getPageSize();
		if(pageNo < WebConstants.DEFAULT_PAGENO) pageNo = WebConstants.DEFAULT_PAGENO;
		if(pageSize < 0) pageSize = WebConstants.DEFAULT_PAGESIZE;
		page.setPageNo(pageNo);
		page.setPageSize(pageSize);
		listVO.setPageNo(pageNo);
		listVO.setPageSize(pageSize);
	}
	
	/**
	 * 设置加载记录信息
	 * @param q
	 * @param page
	 * @return
	 */
	public Query setPageParameter(final Query q, final BaseListVO listVO) {
		if(!listVO.isFindAll()){
			q.setFirstResult(listVO.getFirstResult());
			q.setMaxResults(listVO.getPageSize());
		}
		return q;
	}
	
	@SuppressWarnings("unchecked")
	public Object namedQuery(String hqlName, Map params){
		return null;
	}
	
   public Query createSQLQuery(final String sql, final Object... values) {
        Query query = getSession().createSQLQuery(sql).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query;
    }
    
    public SQLQuery createSQLQuery_(final String sql, final Object... values) {
        SQLQuery query = getSession().createSQLQuery(sql);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query;
    }
    
    public int countSqlResult(final String sql, final Object... values) {
         String countSql = prepareCountHql(sql);
         try {
        	 Object obj = createSQLQuery_(countSql, values).uniqueResult();
        	 if(obj instanceof BigDecimal){
        		 BigDecimal count = (BigDecimal)obj;
        		 return count.intValue();
        	 }else if(obj instanceof BigInteger){
        		 BigInteger count = (BigInteger)obj;
        		 return count.intValue();
        	 }else{
        		 BigDecimal count = (BigDecimal)obj;
        		 return count.intValue();
        	 }
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException("sql can't be auto count, sql is:" + countSql, e);
         }
    }   
    
    protected String prepareCountHql(String orgHql) {
        String countHql = "select count(1) from (" + orgHql + ") t0";
        return countHql;
    }
    
}
